<template>
  <template v-if="isModal">
    <n-drawer v-model:show="showForm as boolean" :width="wSize" :mask-closable="!showConfirmBtn" @after-enter="() => (formLoading = false)">
      <n-drawer-content>
        <template #header> {{ title || formTypeTitleMap[formType || ''] || '' }}</template>
        <n-spin :show="formLoading">
          <slot name="top" />
          <div v-if="!formLoading || !lazyForm" class="form-content">
            <div :style="{ width: formWidth }">
              <n-form ref="formRef" v-bind="formProps" :model="formData" label-placement="left" :inline="columns !== 1" label-width="auto">
                <n-grid :cols="columns" :x-gap="12">
                  <n-form-item-gi v-for="item in jsonScheme" :key="item.path || item.key" v-bind="createFormItemProps(item)">
                    <component :is="createInpComp(item)" />
                  </n-form-item-gi>
                </n-grid>
              </n-form>
            </div>
            <div class="form-content-right">
              <slot name="right" />
            </div>
          </div>
          <slot />
        </n-spin>
        <template #footer>
          <div class="e-form-bottom" style="width: 100%">
            <div class="e-form-bottom-left">
              <n-space>
                <n-button v-if="showConfirmBtn" type="primary" secondary :loading="formLoading" @click="submitHandle">{{ okBtn }}</n-button>
                <n-button type="default" secondary @click="close">{{ cancelBtn }}</n-button>
              </n-space>
            </div>
            <div class="e-form-bottom-right">
              <slot name="bottom" />
            </div>
          </div>
        </template>
      </n-drawer-content>
    </n-drawer>
  </template>
  <template v-else>
    <slot name="top" />
    <n-form ref="formRef" v-bind="formProps" :model="formData" label-placement="left" :inline="columns !== 1" label-width="auto">
      <n-grid :cols="columns" :x-gap="12">
        <n-form-item-gi v-for="item in jsonScheme" :key="item.path || item.key" v-bind="createFormItemProps(item)">
          <component :is="createInpComp(item)" />
        </n-form-item-gi>
      </n-grid>
    </n-form>
    <slot />
  </template>
</template>

<script lang="ts" setup generic="T extends BaseModel">
  import { BaseModel } from '@/hooks/useModel'
  import type { FormInst, FormItemGiProps } from 'naive-ui'
  import { createInputComponent } from './index'
  import { appSetting, formTypeTitleMap } from '@/config/app.config'
  import { cloneDeep, isArray } from 'lodash-es'
  import { useModelApi } from '@/hooks/useApi'
  import type { FieldOption, FormType, IFormData } from 'easy-descriptor'
  import { FormTypeEnum } from 'easy-descriptor'
  import { CreateInputComponentParam, EFormInst, EFormProps, FormatFormData } from './types'
  import { HttpRequest } from 'easy-descriptor/dist/descriptor/AxiosDescriptor/types'
  import { EzFieldOption } from '@/hooks/useModel/types'

  defineOptions({
    name: 'EForm'
  })

  const formRef = ref<FormInst>()
  /**
   * 表单类型
   * @see FormType
   */
  const formType = ref<FormType>()

  const formData = ref<IFormData<T>>()

  const {
    instance,
    isModal = true,
    formWidth = '100%',
    formProps = () => ({
      size: appSetting.formSize
    }),
    formatFormData = (async (data: IFormData<T>) => cloneDeep(data)) as FormatFormData<T>,
    beforeSubmit = (async (data: IFormData<T>) => cloneDeep(data)) as FormatFormData<T>,
    okBtn = '保存',
    cancelBtn = '关闭',
    modelOptions,
    defaultData,
    size,
    cols
  } = defineProps<EFormProps<T>>()

  // const modelState = modelOptions

  const jsonScheme = computed<FieldOption<T>[]>(() =>
    Object.values(modelOptions?.fields || []).filter(({ hidden, hiddenHandler, key }) => {
      const flag1 = !(hidden === true || (hidden && hidden?.includes('form')) || (formType?.value && hidden && hidden?.includes(formType.value as any)))
      const flag2 = !hiddenHandler || !hiddenHandler(formData.value as IFormData<T>, formType.value)
      return flag1 && flag2 && !(key as string).startsWith('$')
    })
  )
  /**
   * 表单列数
   */
  const columns = computed<number>(() => Math.min(cols ?? Math.ceil(jsonScheme.value.length / appSetting.formMaxRows), appSetting.formMaxCols))

  /**
   * 表单开窗大小
   */
  const wSize = computed<number | string>(() => size ?? Math.max(columns.value * appSetting.formColWidth, 550))

  /**
   * 表单加载中状态标记
   * 1.打开抽屉 --> 动态创建表单项完成
   * 2.提交表单 --> 提交完成
   */
  const formLoading = ref<boolean>(true)

  /**
   * 是否展示确认按钮：查看表单不展示
   */
  const showConfirmBtn = computed<boolean>(() => !(formType.value === FormTypeEnum.VIEW || !okBtn))

  /**
   * 表单显示状态
   */
  const showForm: Ref<boolean> = defineModel<boolean>('show', { default: false })

  // const createInpComp = (field: FieldOption<T>) => createInputComponent<T>(field, formData, formType.value)
  const createInpComp = (field: FieldOption<T>) =>
    createInputComponent<T>({
      field,
      formData: formData,
      target: instance,
      formType: formType.value
    } as CreateInputComponentParam<T>)

  /**
   * 根据字段选项创建表单项的属性对象。
   *
   * 该函数用于根据提供的字段选项（FieldOption）生成表单项（FormItem）所需的属性（FormItemGiProps）。
   * 这些属性包括表单项的路径、验证规则、是否必填、标签等信息，用于在表单中正确渲染和验证表单项。
   *
   * @param field 字段选项对象，包含表单项的各种配置信息，如键名、路径、验证规则等。
   * @returns 返回一个对象，包含表单项在表单中的各种属性，如路径、验证规则、是否必填、标签和占据的栅格数。
   */
  const createFormItemProps = (field: EzFieldOption<T>): FormItemGiProps => {
    // 解构字段选项中的关键属性
    const { key, path, rulePath, required, formSpan, label, formItemProps: { ruleHandler, rule, ...formItemProps } = {} } = field
    // 初始化规则数组
    const _rule: any[] = []
    // 根据规则的类型和适用的表单类型，过滤并构建有效的验证规则数组
    if (rule) {
      if (isArray(rule)) {
        _rule.push(...rule)
      } else {
        _rule.push(rule)
      }
      // if (isArray(rule)) {
      //   _rule.push(...rule.filter(({ formTypes }) => !formTypes || formTypes.includes(formType.value as any)))
      // } else if (!rule.formTypes || rule.formTypes.includes(formType.value as any)) {
      //   _rule.push(rule)
      // }
    }
    // console.log(`1_rule[${key}]:`, _rule)
    if (typeof ruleHandler === 'function') {
      const rules = ruleHandler({
        key: key ?? '',
        formData,
        formType: formType.value!
      })
      console.log('rules', rules)
      _rule.push(...rules)
    }
    // console.log(`2_rule[${key}]:`, _rule)
    // 生成并转换表单项的路径或键名，确保其为字符串类型
    const _key = (path || key)?.toString()
    // 返回表单项的属性对象
    return { path: _key, rule: _rule, rulePath, required, label, span: formSpan, ...formItemProps }
  }

  /**
   * 打开 抽屉
   * @param type
   * @param fData
   */
  const open = async (type: FormType, fData?: IFormData<T>) => {
    console.debug(`form show: type = ${type}, data = `, fData || {})
    formType.value = type
    showForm.value = true
    formLoading.value = true
    if (fData) {
      formatFormData(fData, type).then((data) => {
        formData.value = {
          ...cloneDeep(defaultData),
          ...data
        } as IFormData<T>
      })
    } else {
      formData.value = {
        ...cloneDeep(defaultData),
        ...fData
      } as IFormData<T>
    }
  }

  /**
   * 关闭 抽屉
   */
  const close = (clearForm?: boolean) => {
    showForm.value = false
    formLoading.value = false
    clearForm && (formData.value = {})
    return Promise.resolve()
  }

  const emits = defineEmits<{
    (e: 'success', resp?: R, formType?: FormType): void
    (e: 'error', error: unknown, resp?: R, formType?: FormType): void
  }>()

  const { save, update } = useModelApi<T>(modelOptions.api)
  const submitHandle = async () => {
    formLoading.value = true

    const validate = await formRef.value?.validate().catch((errors) => {
      formLoading.value = false
      if (errors && errors.length) {
        return false
      }
    })
    console.log('validate', validate)
    if (validate === false) {
      return
    }
    // console.log('errors', errors)
    // if (errors) {
    //   formLoading.value = false
    //   return
    // }

    const formDataClone = await beforeSubmit(formData.value || {}, formType.value)

    if (!formDataClone) {
      formLoading.value = false
      return
    }
    let submit: HttpRequest | null = null
    switch (formType.value) {
      case FormTypeEnum.ADD:
        submit = save
        // resp = await save(formDataClone)
        break
      case FormTypeEnum.EDIT:
        submit = update
        // resp = await update(formDataClone)
        break
      default:
        await close()
        break
    }
    if (submit) {
      const resp = await submit(formDataClone)
        .catch((error) => {
          console.warn('submitHandle: ', error)
          emits('error', error, undefined, formType.value)
          return
        })
        .finally(() => {
          formLoading.value = false
        })
      if (resp) {
        emits('success', resp, formType.value!)
        window.$message.success(resp.message ?? '操作成功')
        await close(true)
      }
    }
    // if (formDataClone === false) return
  }

  defineExpose<EFormInst<T>>({
    open,
    close,
    getFormData: () => formData.value as IFormData<T>,
    setFormData: (data: IFormData<T>) => {
      formData.value = { ...formData.value, ...data }
    }
  })

  defineSlots<{
    top?: (props?: {}) => any
    default?: (props?: {}) => any
    bottom?: (props?: {}) => any
    right?: (props?: {}) => any
  }>()
</script>

<style scoped lang="less">
  .e-form-bottom {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    overflow: hidden;
  }

  .form-content {
    display: flex;

    .form-content-right {
      flex: 1;
    }
  }
</style>
